/*
 *  (c) E.S.A.
 */

#include "remote_party.h"
#include "socket_utilities.h"
#include "msg_logger.h"

#include <iostream>

/**  ~~~~~~~~~~~~~~~ REMOTE SERVERS ~~~~~~~~~~~~~~~~ */

remote_srv::remote_srv ( const std::string& remote_ip,
		int remote_port )
:ip(remote_ip),
port(remote_port)
{
	state = DISCONN;

	sem_init ( &buffer.full_sem, 0, 0);
	sem_init ( &buffer.empty_sem,0, CIRCULAR_BUF_SIZE);
	sem_init ( &buffer.put_mutex, 0, 1);
	sem_init ( &buffer.get_mutex, 0, 1);
	buffer.nextin = buffer.nextout = 0;
}

remote_srv::~remote_srv()
{
	sem_destroy ( &buffer.full_sem);
	sem_destroy ( &buffer.empty_sem);
	sem_destroy ( &buffer.put_mutex);
	sem_destroy ( &buffer.get_mutex);
}

void remote_srv::connect()
{
	sock = create_socket ();
	sock = connect_to_server (sock, ip, port); 
	if (sock != -1)
		state = CONN;

}


void remote_srv::write_to_remote_srv(const std::string& data)
{
	send_data_len_prefixed (sock, data);
}

void remote_srv::send_data_to_remote_srv()
{
	if ( sem_trywait( &buffer.full_sem ) == 0 ) { 
		sem_wait( &buffer.get_mutex);
		MSG_LOG ( DBG, "writing data to remote server.. ");
		// send message on socket
		write_to_remote_srv (buffer.mess[buffer.nextout]);

		buffer.nextout = (buffer.nextout + 1) % CIRCULAR_BUF_SIZE;

		sem_post ( &buffer.get_mutex);
		sem_post ( &buffer.empty_sem);

	}
}

void remote_srv::add_data_to_buffer(const std::string& data)
{
	sem_wait( &buffer.empty_sem );
	sem_wait( &buffer.put_mutex );

	buffer.mess[buffer.nextin] = data;
	buffer.nextin = (buffer.nextin + 1) % CIRCULAR_BUF_SIZE;

	sem_post( &buffer.put_mutex );
	sem_post( &buffer.full_sem );
}

int remote_srv::read_from_remote_srv(void* data, int len)
{
	return receive_data_on_socket (sock, data, len);
}

void remote_srv::disconnect()
{
	close(sock);
}

srv_state remote_srv::get_conn_state()
{
	return state;
}

std::string remote_srv::get_ip()
{
	return ip;
}

int remote_srv::get_sock()
{
	return sock;
}

void remote_srv::print()
{
	MSG_LOG (INFO, "IP : " << ip);
	MSG_LOG (INFO, "PORT : " << port);
	MSG_LOG (INFO, "STATE : " << state);
}

remote_srv_list::remote_srv_list()
{
	/* initiat the mutex */
	if (pthread_mutex_init(&mutex, NULL) != 0){
		MSG_LOG (ERR, "error at initiating the mutex");
	}
}

remote_srv_list::~remote_srv_list()
{
	typedef std::map<std::string, remote_srv*>::iterator container_iterator;
	container_iterator it;

	if(pthread_mutex_destroy(&mutex) != 0){
		MSG_LOG (ERR, "cannot destroy mutex for interfaces list");
	}

	if (container.empty()) return;

	for (it = container.begin(); it != container.end(); ++it){
		remote_srv* temp_point = it->second;
		delete temp_point;
	}
	container.clear();
}

void remote_srv_list::lock_list() const 
{
	int rez = pthread_mutex_lock(&mutex);
	if(rez != 0){
		MSG_LOG (ERR, "could not acquire mutex");
	}
}

void remote_srv_list::unlock_list() const 
{
	int rez = pthread_mutex_unlock(&mutex);
	if(rez != 0){
		MSG_LOG (ERR, "could not release mutex");
	}

}

void remote_srv_list::add_element(remote_srv* server)
{
	lock_list();
	container.insert(make_pair(server->get_ip(), server));
	unlock_list();
}

remote_srv* remote_srv_list::get_element(const std::string& ip) const
{
	typedef std::map<std::string, remote_srv*>::const_iterator container_iterator;

	container_iterator it = container.find(ip);
	if(it == container.end())
		return NULL;

	return it->second;
}

void remote_srv_list::del_element(const std::string& ip)
{
	typedef std::map<std::string, remote_srv*>::iterator container_iterator;

	lock_list();
	container_iterator it = container.find(ip);
	if(it != container.end())
		container.erase(it);
	unlock_list();
}


/**  ~~~~~~~~~~~~~~~~~ REMOTE CLIENTS ~~~~~~~~~~~~~~~~~ */

remote_cli_list::remote_cli_list()
{
	/* initiat the mutex */
	if (pthread_mutex_init(&mutex, NULL) != 0){
		MSG_LOG (ERR, "error at initiating the mutex");
	}
}

remote_cli_list::~remote_cli_list()
{
	typedef std::map<std::string, r_client_data*>::iterator container_iterator;
	container_iterator it;

	if(pthread_mutex_destroy(&mutex) != 0){
		MSG_LOG (ERR, "cannot destroy mutex for interfaces list");
	}

	if (container.empty()) return;

	//for (it = container.begin(); it != container.end(); ++it){
		//r_client_data* temp_point = it->second;
		//delete temp_point;
	//}
	container.clear();
}

void remote_cli_list::lock_list() const 
{
	int rez = pthread_mutex_lock(&mutex);
	if(rez != 0){
		MSG_LOG (ERR, "could not acquire mutex");
	}
}

void remote_cli_list::unlock_list() const 
{
	int rez = pthread_mutex_unlock(&mutex);
	if(rez != 0){
		MSG_LOG (ERR, "could not release mutex");
	}

}

void remote_cli_list::add_element(r_client_data* remote_cli)
{
	lock_list();
	container.insert(make_pair(remote_cli->ip, remote_cli));
	unlock_list();
}

r_client_data* remote_cli_list::get_element(const std::string& ip) const
{
	typedef std::map<std::string, r_client_data*>::const_iterator container_iterator;

	container_iterator it = container.find(ip);
	if(it == container.end())
		return NULL;

	return it->second;
}

void remote_cli_list::del_element(const std::string& ip)
{
	typedef std::map<std::string, r_client_data*>::iterator container_iterator;

	lock_list();
	container_iterator it = container.find(ip);
	if(it != container.end())
		container.erase(it);
	unlock_list();
}

